
Assistant = function(spec) {
    $.extend(this, spec);
    this.environmentSignalHandler = $.proxy(this.environmentSignal, this);
    this.tasks = $.map(this.tasks, function(task) { return new Task(task); });
    this.data = {};
    this.computeTaskGroups();
    this.start();
};

$.extend(Assistant.prototype, {
    environmentSignal: function(event) {
        if (!event.location.lng) console.log(event.location);
        var d = this.previousEvent ? new GeoPoint(event.location.lat, event.location.lng).distance(this.previousEvent.location) : null;
        if (d === null || d > 300) {
            this.previousEvent = event;
            $.each(this.taskGroups, $.proxy(this.processTaskGroup, this, event));
        }
    },

    processTaskGroup: function(event, _, g) {
        if (g.hasLocation()) {
            // If distance from task group is too large,
            // don't bother dealing with the task group.
            var d = g.distance(event.location);
            if (d > 300) {
                return;
            }

            var data = $(this.data);
            $.each(g.tasks, function(_, task) {
                var d = new GeoPoint(task.location.lat, task.location.lng).distance(event.location);
                if (d <= 300) {
                    var action = $.extend({ time: event.time }, task);
                    data.trigger('suggest', action);
                }
            });
        } else {
            var placeTypes = [];
            var placeTypeHash = {};
            $.each(g.tasks, function(_, task) {
                var items = task.item ? [task.item] : task.items;
                if (items) {
                    var types = PlaceTypes.selling(items);
                    $.each(types, function(_, type) {
                        if (!placeTypeHash[type]) {
                            placeTypeHash[type] = 1;
                            placeTypes.push(type);
                        }
                    });
                }
            });

            this.environment.places({
                types: placeTypes,
                location: event.location,
                handler: $.proxy(this.places, this, event, g)
            });
        }
    },

    places: function(event, taskGroup, places) {
        var data = $(this.data);
        var p = new GeoPoint(event.location.lat, event.location.lng);
        $.each(places, function(_, place) {
            var q = new GeoPoint(place.lat, place.lng);
            if (p.distance(q) <= 300) {
                $.each(taskGroup.tasks, function(_, task) {
                    var items = task.item ? [task.item] : task.items;
                    if (items) {
                        var types = PlaceTypes.selling(items);
                        types = $.map(types, function(type) { return type.name; });
                        types = types.hash();
                        var placeTypes = $.isArray(place.type) ? place.type : [place.type];
                        var typeValid = !!$.grep(placeTypes, function(t) { return types[t.name]; }).length;
                        if (typeValid) {
                            var args = $.extend({ time: event.time, location: place }, task);
                            data.trigger('suggest', Action[task.action](args));
                        }
                    }
                });
            }
        });
    },

    start: function() {
        this.environment.signal(this.environmentSignalHandler);
    },

    suggest: function(handler) {
        $(this.data).on('suggest', function(event, spec) {
            handler(spec);
        });
    },

    computeTaskGroups: function() {
        // todo: compute actual task groups by distance.
        // For now, return singleton groups.
        var taskGroups = $.map(this.tasks, function(task) {
            return {
                lat: task.location && task.location.lat,
                lng: task.location && task.location.lng,
                tasks: [ task ],

                distance: function(p) {
                    return new GeoPoint(this.lat, this.lng).distance(p);
                },

                hasLocation: function() {
                    return (this.lat || this.lat == 0) && (this.lng || this.lng == 0);
                }
            };
        });

        this.taskGroups = $.map(taskGroups, function(g) {
            return new TaskGroup(g);
        });
    }
});

$.extend(Assistant, {});
